#if !defined(__INSTANTANE_DU_PIANO_H)
#define __INSTANTANE_DU_PIANO_H

#include "definitions.h"
#include "temps/weather_report.h"
#include <algorithm>
#include <string>
#include <iostream>

/**
 * Met les choses au clair. Le questionnement à la base était que le standard
 * ne définit pas le nombre de bits de l'unité élémentaire (char), mais que
 * les sockets utilisaient des char de 8 bits (on suppose), et qu'il fallait
 * donc s'adapter à quelque chose avec un nombre fixé de bits peu importe
 * le système hôte et ses caractéristiques.
 */
typedef Uint8 type_octet;

/**
 * static_assert, une création Ras Patrice Roy-fari
 */
template <bool>
struct static_assert;
template <>
struct static_assert<true> {};

/**
 * Avant le cours de STR, tu n'as jamais vu ça de ta vie.
 * Après le cours de STR, tu trouves ça d'une banalité attristante !
 */
template <int n>
struct nb_octets_pour_ {
	static_assert<(n > 0)> n_doit_etre_strictement_positif;
	enum { touches = (n-1)/8 + 1 };
};

class instantane_du_piano;

/**
 * Foncteur de remplissage d'un tableau d'octets pour l'écriture sur un tableau
 * d'octets brut.
 */
class remplisseur {
	int octet_actuel_;
	instantane_du_piano &inst_;
public:
	remplisseur(instantane_du_piano &i) throw() : inst_(i), octet_actuel_(0) {}
	type_octet operator()();
};

/**
 * Foncteur d'intialisation à partir d'un tableau d'octets.
 */
class initialiseur {
	int octet_actuel_;
	instantane_du_piano &inst_;
public:
	initialiseur(instantane_du_piano &i) throw() : inst_(i), octet_actuel_(0) {}
	void operator()(type_octet o);
};

/**
 * Représente un "instantané" du piano, c'est-à-dire l'état global de toutes
 * les touches du piano à un instant donné par le timestamp.
 * C'est ce qui sera envoyé par le réseau.
 */
struct instantane_du_piano
{
	enum { nb_octets = nb_octets_pour_<NB_TOUCHES_PIANO>::touches };
	enum { nombre_de_touches = NB_TOUCHES_PIANO };
private:
	/**
	 * Variables d'instance
	 */
	temps::type_temps timestamp_;
	bool touches[nombre_de_touches];
	
public:
	/**
	 * Itérateur ne suivant pas totalement la sémantique STL ni compatible avec elle, 
	 * mais qui sert à itérer parmi les touches actives d'un instantané donné.
	 */
	class iterator_touches_actives {
		const instantane_du_piano &inst_;
		int derniere_touche_;
	public:
		iterator_touches_actives(const instantane_du_piano* inst) : inst_(*inst), derniere_touche_(0) {}
		
		iterator_touches_actives& operator++() {
			for(derniere_touche_++; !inst_[derniere_touche_] && derniere_touche_<inst_.nombre_de_touches; derniere_touche_++);
			return *this;
		}
		
		touche_du_piano operator*() {
			return static_cast<touche_du_piano>(derniere_touche_);
		}
		
		bool operator()() {
			return derniere_touche_ <= pedale_douce;
		}
	};
	
	/*
	 *********** (ça fait une bite ou je rêve ?)
	 */
	
	
	instantane_du_piano() throw() : timestamp_(temps::maintenant()) {
		laver();
	}
	
	void laver() {
		std::fill(touches, touches + nombre_de_touches, false);
	}
	
	bool &operator[](touche_du_piano i) {
		return touches[i];
	}	
	
	bool &operator[](int i) {
		return operator[](static_cast<touche_du_piano>(i));
	}	
	
	bool operator[](touche_du_piano i) const {
		return touches[i];
	}	
	
	bool operator[](int i) const {
		return operator[](static_cast<touche_du_piano>(i));
	}	
	
	temps::type_temps timestamp() const { return timestamp_; }
	void timestamp(temps::type_temps t) { timestamp_ = t; }
	
	void remplir(type_octet *donnees) {
		remplisseur r(*this);
		std::generate(donnees, donnees + nb_octets, r);
	}
	
	void initialiser(type_octet *donnees) {
		initialiseur i(*this);
		std::for_each(donnees, donnees + nb_octets, i);
	}
	
	iterator_touches_actives iterator() {
		return iterator_touches_actives(this);
	}
	
	instantane_du_piano &operator=(const instantane_du_piano &r) {
		if(this != &r) {
			for(int t = do1; t < nombre_de_touches; ++t)
				touches[t] = r[t];
			timestamp_ = r.timestamp();
		}
		return *this;
	}
	
	friend std::ostream &operator<<(std::ostream &, instantane_du_piano&);
};

#endif
